package com.shiku.imserver.common.ws;

import com.shiku.imserver.common.Protocol;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.core.exception.AioDecodeException;
import org.tio.core.utils.ByteBufferUtils;
import org.tio.websocket.common.Opcode;

public class WsServerDecoder {
    private static Logger log = LoggerFactory.getLogger(WsServerDecoder.class);

    public enum Step {
        header,
        remain_header,
        data
    }

    public static WsRequest decode(ByteBuffer buf, ChannelContext channelContext) throws AioDecodeException, UnsupportedEncodingException {
        WsSessionContext imSessionContext = (WsSessionContext) channelContext.getAttribute();
        List<byte[]> lastParts = imSessionContext.getLastParts();
        int readableLength = buf.limit() - buf.position();
        int headLength = 2;
        if (readableLength < 2) {
            return null;
        }
        byte first = buf.get();
        boolean fin = (first & 128) > 0;
        int i = (first & 112) >>> 4;
        Opcode opcode = Opcode.valueOf((byte) (first & Protocol.FIRST_BYTE_MASK_VERSION));
        if (opcode == Opcode.CLOSE) {
        }
        if (!fin) {
            log.error("{} 暂时不支持fin为false的请求", channelContext);
            Tio.remove(channelContext, "暂时不支持fin为false的请求");
            return null;
        }
        imSessionContext.setLastParts(null);
        byte second = buf.get();
        boolean hasMask = ((second & 255) >> 7) == 1;
        if (hasMask) {
            headLength = 2 + 4;
        }
        int payloadLength = second & Byte.MAX_VALUE;
        byte[] mask = null;
        if (payloadLength == 126) {
            headLength += 2;
            if (readableLength < headLength) {
                return null;
            }
            payloadLength = ByteBufferUtils.readUB2WithBigEdian(buf);
        } else if (payloadLength == 127) {
            headLength += 8;
            if (readableLength < headLength) {
                return null;
            }
            payloadLength = (int) buf.getLong();
            log.info("{} payloadLengthFlag: 127，payloadLength {}", channelContext, Integer.valueOf(payloadLength));
        }
        if (payloadLength < 0 || payloadLength > 524288) {
            throw new AioDecodeException("body length(" + payloadLength + ") is not right");
        } else if (readableLength < headLength + payloadLength) {
            return null;
        } else {
            if (hasMask) {
                mask = ByteBufferUtils.readBytes(buf, 4);
            }
            WsRequest websocketPacket = new WsRequest();
            websocketPacket.setWsEof(fin);
            websocketPacket.setWsHasMask(hasMask);
            websocketPacket.setWsMask(mask);
            websocketPacket.setWsOpcode(opcode);
            websocketPacket.setWsBodyLength((long) payloadLength);
            if (payloadLength == 0) {
                return websocketPacket;
            }
            byte[] array = ByteBufferUtils.readBytes(buf, payloadLength);
            if (hasMask) {
                for (int i2 = 0; i2 < array.length; i2++) {
                    array[i2] = (byte) (array[i2] ^ mask[i2 % 4]);
                }
            }
            if (!fin) {
                log.error("payloadLength {}, lastParts size {}, array length {}", new Object[]{Integer.valueOf(payloadLength), Integer.valueOf(lastParts.size()), Integer.valueOf(array.length)});
                return websocketPacket;
            }
            int allLength = array.length;
            if (lastParts != null) {
                for (byte[] part : lastParts) {
                    allLength += part.length;
                }
                byte[] allByte = new byte[allLength];
                int offset = 0;
                for (byte[] part2 : lastParts) {
                    System.arraycopy(part2, 0, allByte, offset, part2.length);
                    offset += part2.length;
                }
                System.arraycopy(array, 0, allByte, offset, array.length);
                array = allByte;
            }
            websocketPacket.setBytes(array);
            if (opcode == Opcode.BINARY) {
                return websocketPacket;
            }
            try {
			    websocketPacket.setWsBodyText(new String(array, "utf-8"));
			    return websocketPacket;
			} catch (UnsupportedEncodingException e) {
				 log.error(e.toString(), e);
	                return websocketPacket;
			}
        }
    }
}
